#region license
# This file is part of Grawlix.
# 
# Grawlix is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
# 
# Grawlix is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public
# License along with Grawlix.  If not, see
# <http://www.gnu.org/licenses/>.
#endregion

"""Create a static proxy which delegates all public properties and fields."""

namespace Grawlix.Meta
import System
import Boo.Lang.Compiler
import Boo.Lang.Compiler.TypeSystem

transient class ProxyAttribute(StagedAstAttribute):
"""Generate members for the class that proxy to a field or property."""
	
	def constructor(
		[required] target as Ast.ReferenceExpression,
		[required] type as Ast.ReferenceExpression):
	"""
	Initializes the attribute.
	Param target: The field or property to proxy.
	Param type: The type to proxy [target] as.
	"""
		_target = target
		_type = Ast.SimpleTypeReference(type.LexicalInfo, type.Name)
	
	override def ProcessSyntax([required] target as Ast.Node):
	"""
	Does simple AST stage processing of the node.
	Param target: The node the attribute is applied to.
	"""
		if Ast.NodeType.ClassDefinition != target.NodeType:
			InvalidNodeForAttribute('ClassDefinition')
			return
		
		_definition = target as Ast.ClassDefinition
	
	override def ProcessDefinition():
	"""
	Does any processing which may generate new types.
	"""
		found = false
		for baseType in _definition.BaseTypes:
			if baseType.Matches(_type) and _type.Matches(baseType):
				found = true
				break
		if not found:
			_definition.BaseTypes.Add(_type)
	
	virtual def ProcessMember():
	"""
	Does member-level processing of the node.
	"""
		NameResolutionService.ResolveTypeReference(_type)
		original = TypeSystemServices.GetEntity(_type) as IType
		
		for member in original.GetMembers():
			method = member as IMethod
			if method is not null:
				ProxyMethod(method)
				continue
			property = member as IProperty
			if property is not null:
				ProxyProperty(property)
				continue
			#eventMember = member as IEvent
			#if eventMember is not null:
			#	ProxyEvent(eventMember)
			#	continue
		
	def ProxyMethod(method as IMethod):
	"""
	Create a proxy for a method.
	Param method: The method to proxy.
	"""
		return if method.IsStatic or not method.IsPublic
		
		# Create method declaration
		proxy = \
			CodeBuilder.CreateMethodFromPrototype(
				LexicalInfo,
				method,
				Ast.TypeMemberModifiers.Public)
		
		# Grab documentation if available
		internalMethod = method as TypeSystem.InternalMethod
		if internalMethod is not null:
			proxy.Documentation = internalMethod.Method.Documentation
		
		# Define method body
		invocation = createDeferredInvocation(proxy, _target, method.Name)
		if returnsValue(method, TypeSystemServices):
			proxy.Body.Add(invocation)
		else:
			body = [| return $invocation |]
			body.LexicalInfo = proxy.LexicalInfo
			proxy.Body.Add(body)
		
	def ProxyProperty(property as IProperty):
	"""
	Create a proxy for a property.
	Param property: The property to proxy.
	"""
		return if property.IsStatic or not property.IsPublic
	
	private _target as Ast.ReferenceExpression
	"""The name of the field or target to reference."""
	
	private _type as Ast.TypeReference
	"""The type to proxy as."""
	
	private _definition as Ast.ClassDefinition
	"""The proxy definition."""
	
	